package com.wosai.register.service;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.wosai.register.model.BranchExpend;
import com.wosai.register.model.BranchExpendSubject;
import com.wosai.register.model.BranchExpendType;
import com.wosai.register.model.Category;
import com.wosai.register.model.EmployeeAccount;
import com.wosai.register.model.EmployeeCash;
import com.wosai.register.model.MerchantBaseEntity;
import com.wosai.register.model.Order;
import com.wosai.register.model.OrderItem;
import com.wosai.register.model.Price;
import com.wosai.register.model.Product;
import com.wosai.register.model.Terminal;
import com.wosai.register.model.Employee;
import com.wosai.register.repository.BranchExpendRepository;
import com.wosai.register.repository.BranchExpendSubjectRepository;
import com.wosai.register.repository.BranchExpendTypeRepository;
import com.wosai.register.repository.CategoryRepository;
import com.wosai.register.repository.OrderItemRepository;
import com.wosai.register.repository.OrderRepository;
import com.wosai.register.repository.PriceRepository;
import com.wosai.register.repository.ProductRepository;
import com.wosai.register.repository.TerminalRepository;
import com.wosai.register.repository.EmployeeRepository;
import com.wosai.register.repository.UserRepository;
import com.wosai.register.util.DateUtil;

@Service
public class CoreServiceImpl implements CoreService {
    private static final Log logger = LogFactory.getLog(CoreServiceImpl.class);
    
    private EmployeeRepository employeeRepository;
    private TerminalRepository terminalRepository;
    private OrderRepository orderRepository;
    private OrderItemRepository orderItemRepository;
    private ProductRepository productRepository;
    private PriceRepository priceRepository;
    private CategoryRepository categoryRepository;
    private BranchExpendTypeRepository branchExpendTypeRepository;
    private BranchExpendSubjectRepository branchExpendSubjectRepository;
    private BranchExpendRepository branchExpendRepository;
    private UserRepository userRepository;

    
    @Autowired
    public CoreServiceImpl(EmployeeRepository employeeRepository,
                           TerminalRepository terminalRepository,
                           OrderRepository orderRepository,
                           OrderItemRepository orderItemRepository,
                           ProductRepository productRepository,
                           PriceRepository priceRepository,
                           CategoryRepository categoryRepository,
                           BranchExpendTypeRepository branchExpendTypeRepository,
                           BranchExpendSubjectRepository branchExpendSubjectRepository,
                           BranchExpendRepository branchExpendRepository,
                           UserRepository userRepository)
    {
        this.employeeRepository = employeeRepository;
        this.terminalRepository = terminalRepository;
        this.orderRepository = orderRepository;
        this.orderItemRepository = orderItemRepository;
        this.productRepository = productRepository;
        this.priceRepository = priceRepository;
        this.categoryRepository = categoryRepository;
        this.branchExpendTypeRepository = branchExpendTypeRepository;
        this.branchExpendSubjectRepository = branchExpendSubjectRepository;
        this.branchExpendRepository = branchExpendRepository;
        this.userRepository = userRepository;
    }


    @Override
    public List<String> insertOrders(List<Order> orders) {
        List<OrderItem> orderItems = new ArrayList<OrderItem>();
        for(Order order: orders){
            List<OrderItem> items = order.getItemList();
            if (items!=null){
                for (OrderItem item: items){
                    item.setMerchantId(order.getMerchantId());
                    item.setBranchId(order.getBranchId());
                    item.setTerminalId(order.getTerminalId());
                    item.setOrderId(order.getId());
                    
                    orderItems.add(item);
                }
            }
            
        }
        this.orderItemRepository.insertBatch(orderItems);
        return this.orderRepository.insertBatch(orders);
    }


    @Override
    public List<String> updateOrders(List<Order> orders) {
        return this.orderRepository.updateBatch(orders);
    }
    
    @Override
    public List<String> saveOrders(List<Order> orders) {
        List<Order> newOrders = new ArrayList<Order>();
        List<Order> oldOrders = new ArrayList<Order>();
        splitOldFromNew(orders, newOrders, oldOrders);
        ArrayList<String> failed = new ArrayList<String>();
        if (!newOrders.isEmpty()){
            failed.addAll(insertOrders(newOrders));
        }
        if (!oldOrders.isEmpty()){
            failed.addAll(updateOrders(oldOrders));
        }
        return failed;
    }


    @Override
    public Order findOrderById(String merchantId, String branchId, String terminalId,
                               String orderId) {
        return this.orderRepository.findById(merchantId, branchId, terminalId, orderId);
    }


    @Override
    public List<Order> findOrdersByTerminal(String merchantId,
                                            String branchId,
                                            String terminalId, int offset,
                                            int size,
                                            String startDate,
                                            String endDate) {
        List<Order> orders = this.orderRepository.findByTerminal(merchantId, branchId, terminalId, offset, size, startDate, endDate);
        for(Order order: orders){
            order.setItemList(findOrderItemsByOrder(merchantId, branchId, terminalId, order.getId()));
        }
        return orders;
    }


    @Override
    public void deleteOrder(String merchantId, String branchId, String terminalId, String orderId) {
        this.orderItemRepository.deleteByOrder(merchantId, branchId, terminalId, orderId);
        this.orderRepository.delete(merchantId, branchId, terminalId, orderId);
    }


    @Override
    public void deleteOrdersByTerminal(String merchantId, String branchId, String terminalId) {
        this.orderItemRepository.deleteByTerminal(merchantId, branchId, terminalId);
        this.orderRepository.deleteByTerminal(merchantId, branchId, terminalId);
    }


    @Override
    public void insertOrderItems(List<OrderItem> orderItems) {
        this.orderItemRepository.insertBatch(orderItems);
        
    }


    @Override
    public void updateOrderItems(List<OrderItem> orderItems) {
        this.orderItemRepository.updateBatch(orderItems);
    }


    @Override
    public List<OrderItem> findOrderItemsByOrder(String merchantId,
                                                 String branchId,
                                                 String terminalId,
                                                 String orderId) {
        return this.orderItemRepository.findByOrderId(merchantId, branchId, terminalId, orderId);
    }


    @Override
    public List<OrderItem> findOrderItemsByTerminal(String merchantId,
                                                    String branchId,
                                                    String terminalId,
                                                    int offset,
                                                    int size)
    {
        return this.orderItemRepository.findByTerminal(merchantId, branchId, terminalId, offset, size);
    }


    @Override
    public void deleteOrderItemsByOrder(String merchantId, String branchId, String terminalId,
                                String orderId) {
        this.orderItemRepository.deleteByOrder(merchantId, branchId, terminalId, orderId);
    }


    @Override
    public void deleteOrderItemsByTerminal(String merchantId, String branchId, String terminalId) {
        this.orderItemRepository.deleteByTerminal(merchantId, branchId, terminalId);
    }


    @Override
    public List<String> insertProducts(List<Product> products) {
        List<Price> pl = new ArrayList<Price>();
        for(Product product: products) {
            List<Price> prices = product.getPrices();
            if (prices!=null){
                for(Price price: prices){
                    price.setMerchantId(product.getMerchantId());
//                    price.setTerminalId(product.getTerminalId());
                    price.setProductId(product.getId());
                    pl.add(price);
                }
            }
            
        }
        if (!pl.isEmpty())
            this.priceRepository.insertBatch(pl);
        return this.productRepository.insertBatch(products);
    }


    @Override
    public List<String> updateProducts(List<Product> products) {
        List<Price> pl = new ArrayList<Price>();
        for(Product product: products) {
            List<Price> prices = product.getPrices();
            if (prices!=null){
                for(Price price: prices){
                    price.setMerchantId(product.getMerchantId());
//                    price.setTerminalId(product.getTerminalId());
                    price.setProductId(product.getId());
                    pl.add(price);
                }
            }
            //if ("1".equals(product.getMorePrice()))
                this.priceRepository.deleteByProduct(product.getMerchantId(), product.getId());
        }
        if (!pl.isEmpty())
            this.priceRepository.insertBatch(pl);
        return this.productRepository.updateBatch(products);
    }

    @Override
    public List<String> saveProducts(List<Product> products) {
        List<Product> newProducts = new ArrayList<Product>();
        List<Product> oldProducts = new ArrayList<Product>();
        splitOldFromNew(products, newProducts, oldProducts);
        List<String> failed = new ArrayList<String>();
        if (!newProducts.isEmpty())
            failed.addAll(insertProducts(newProducts));
        if (!oldProducts.isEmpty())
            failed.addAll(updateProducts(oldProducts));
        return failed;
        
    }

    @Override
    public Product findProductById(String merchantId,
                                   String productId) {
        return this.productRepository.findById(merchantId, productId);
    }


    @Override
    public List<Product> findProductsByTerminal(String merchantId) {
        List<Product> products = this.productRepository.findByTerminal(merchantId);
        for(Product product: products)
            product.setPrices(findPricesByProductId(merchantId, product.getId()));
        return products;
    }


    @Override
    public void deleteProduct(String merchantId,
                              String productId) {
        this.productRepository.delete(merchantId, productId);
    }


    @Override
    public void deleteProductsByTerminal(String merchantId) {
        deletePricesByTerminal(merchantId);
        this.productRepository.deleteByTerminal(merchantId);        
    }


    @Override
    public void insertPrices(List<Price> prices) {
        this.priceRepository.insertBatch(prices);
    }


    @Override
    public void updatePrices(List<Price> prices) {
        this.priceRepository.updateBatch(prices);
    }


    @Override
    public List<Price> findPricesByProductId(String merchantId,
                                             String productId) {
        return this.priceRepository.findByProductId(merchantId, productId);
    }


    @Override
    public List<Price> findPricesByTerminal(String merchantId) {
        return this.priceRepository.findByTerminal(merchantId);
    }


    @Override
    public void deletePricesByProduct(String merchantId,
                                      String productId) {
        this.priceRepository.deleteByProduct(merchantId, productId);
        
    }


    @Override
    public void deletePricesByTerminal(String merchantId) {
        this.priceRepository.deleteByTerminal(merchantId);   
    }


    @Override
    public List<String> insertCategories(List<Category> categories) {
        return this.categoryRepository.insertBatch(categories);
    }


    @Override
    public List<String> updateCategories(List<Category> categories) {
        return this.categoryRepository.updateBatch(categories);
    }

    @Override
    public List<String> saveCategories(List<Category> categories) {
        List<Category> newCategories = new ArrayList<Category>();
        List<Category> oldCategories = new ArrayList<Category>();
        splitOldFromNew(categories, newCategories, oldCategories);
        List<String> failed = new ArrayList<String>();
        if (!newCategories.isEmpty())
            failed.addAll(insertCategories(newCategories));
        if (!oldCategories.isEmpty())
            failed.addAll(updateCategories(oldCategories));
        return failed;
    }

    @Override
    public Category findCategoryById(String merchantId,
                                     String categoryId) {
        
        return this.categoryRepository.findById(merchantId, categoryId);
    }


    @Override
    public List<Category> findCategoriesByTerminal(String merchantId) {
        return this.categoryRepository.findByTerminal(merchantId);
    }


    @Override
    public void deleteCategory(String merchantId,
                               String categoryId) {
        this.categoryRepository.delete(merchantId, categoryId);
    }


    @Override
    public void deleteCategoriesByTerminal(String merchantId) {
        this.categoryRepository.deleteByTerminal(merchantId);
    }


    @Override
    public List<String> insertTerminals(List<Terminal> terminals) {
        return this.terminalRepository.insertBatch(terminals);
    }


    @Override
    public List<String> updateTerminals(List<Terminal> terminals) {
        return this.terminalRepository.updateBatch(terminals);
    }

    @Override
    public List<String> saveTerminals(List<Terminal> terminals) {
        List<Terminal> newTerminals = new ArrayList<Terminal>();
        List<Terminal> oldTerminals = new ArrayList<Terminal>();
        splitOldFromNew(terminals, newTerminals, oldTerminals);
        List<String> failed = new ArrayList<String>();
        if(!newTerminals.isEmpty())
            failed.addAll(insertTerminals(newTerminals));
        if(!oldTerminals.isEmpty())
            failed.addAll(updateTerminals(oldTerminals));
        return failed;
    }

    @Override
    public Terminal findTerminal(String merchantId, String branchId, String terminalId) {
        return this.terminalRepository.findById(merchantId, branchId, terminalId);
    }


    @Override
    public void deleteTerminal(String merchantId, String branchId, String terminalId) {
        this.terminalRepository.delete(merchantId, branchId, terminalId);
    }


    @Override
    public List<String> insertEmployees(List<Employee> employees) {
        return this.employeeRepository.insertBatch(employees);
    }


    @Override
    public List<String> updateEmployees(List<Employee> employees) {
        return this.employeeRepository.updateBatch(employees);
    }

    @Override
    public List<String> saveEmployees(List<Employee> employees) {
        List<Employee> newEmployees = new ArrayList<Employee>();
        List<Employee> oldEmployees = new ArrayList<Employee>();
        splitOldFromNew(employees, newEmployees, oldEmployees);
        List<String> failed = new ArrayList<String>();
        if (!newEmployees.isEmpty())
            failed.addAll(insertEmployees(newEmployees));
        if (!oldEmployees.isEmpty())
            failed.addAll(updateEmployees(oldEmployees));
        return failed;
    }

    @Override
    public Employee findEmployeeById(String merchantId,
                                     String employeeId) {
        return this.employeeRepository.findById(merchantId, employeeId);
    }


    @Override
    public List<Employee> findEmployeesByTerminal(String merchantId) {
        return this.employeeRepository.findByTerminal(merchantId);
    }


    @Override
    public void deleteEmployee(String merchantId,
                               String employeeId) {
        this.employeeRepository.delete(merchantId, employeeId);
    }


    @Override
    public void deleteEmployeesByTerminal(String merchantId) {
        this.employeeRepository.deleteByTerminal(merchantId);
    }

    
    private <T extends MerchantBaseEntity> void splitOldFromNew(List<T> all, List<T> newOnes, List<T> oldOnes){
        for(T one: all){
            if ("1".equals(one.getSaveFlag())){
                newOnes.add(one);
            }else if ("2".equals(one.getSaveFlag())){
                oldOnes.add(one);
            }else{
                logger.error("Something is wrong in your object.");
            }
        }
    }
    
    
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    private static String formatDate(Date date) {
        return dateFormat.format(date);
    }

    private static final Random random = new Random();
    @Override
    public String createActivationCode(String merchantId,
                                     String branchId,
                                     String terminalId) {
        
        int randomInt = random.nextInt();
        randomInt = randomInt < 0? -randomInt: randomInt;
        String code = String.format("%06d", randomInt%100000);
        Terminal terminal = findTerminal(merchantId, branchId, terminalId);
        terminal.setActivationCode(code);
        this.terminalRepository.update(terminal);
        return code;
    }


    @Override
    public Map<String, Object> doActivation(String code) {
        Terminal terminal = this.terminalRepository.findByActivationCode(code);
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("products", findProductsByTerminal(terminal.getMerchantId()));
        result.put("categories", findCategoriesByTerminal(terminal.getMerchantId()));
        result.put("employees", findEmployeesByTerminal(terminal.getMerchantId()));
        
        Timestamp now = DateUtil.now();
        Timestamp oneMonthAgo = DateUtil.ndaysFromNow(now, -30);
        
        result.put("orders", findOrdersByTerminal(terminal.getMerchantId(), terminal.getBranchId(), terminal.getTerminalId(),
                                                  0, 1000000, formatDate(oneMonthAgo), formatDate(now)));
        result.put("terminal", terminal);
        return result;
    }


    @Override
    public void doneActivation(String code) {
        this.terminalRepository.clearActivationCode(code);
        
    }


    @Override
    public void changePassword(String username, String password) {
        this.userRepository.updatePassword(username, password);
    }


    @Override
    public List<Map<String, Object>> findTerminalsByUser(String username) {
        return this.userRepository.findTerminalsByUser(username);
   }


    @Override
    public List<EmployeeCash> findEmployeeCashesByTerminal(String merchantId,
                                                           String branchId,
                                                           String terminalId) {
        
        return this.employeeRepository.findCashesByTerminal(merchantId, branchId, terminalId);
    }


    @Override
    public void saveEmployeeCashes(List<EmployeeCash> employeeCashes) {
        this.employeeRepository.insertCashBatch(employeeCashes);
    }


    @Override
    public List<EmployeeAccount> findEmployeeAccountsByTerminal(String merchantId,
                                                                String branchId,
                                                                String terminalId) {
        return this.employeeRepository.findAccountsByTerminal(merchantId, branchId, terminalId);
    }


    @Override
    public void saveEmployeeAccounts(List<EmployeeAccount> employeeAccounts) {
        this.employeeRepository.insertAccountBatch(employeeAccounts);
    }


    @Override
    public List<String> insertBranchExpends(List<BranchExpend> expends) {
        return this.branchExpendRepository.insertBatch(expends);
    }


    @Override
    public List<String> updateBranchExpends(List<BranchExpend> expends) {
        return this.branchExpendRepository.updateBatch(expends);
    }


    @Override
    public List<String> saveBranchExpends(List<BranchExpend> expends) {
        List<BranchExpend> newExpends = new ArrayList<BranchExpend>();
        List<BranchExpend> oldExpends = new ArrayList<BranchExpend>();
        splitOldFromNew(expends, newExpends, oldExpends);
        ArrayList<String> failed = new ArrayList<String>();
        if (!newExpends.isEmpty()){
            failed.addAll(insertBranchExpends(newExpends));
        }
        if (!oldExpends.isEmpty()){
            failed.addAll(updateBranchExpends(oldExpends));
        }
        return failed;
    }

    @Override
    public List<BranchExpend> findBranchExpends(String merchantId,
                                                String branchId,
                                                String terminalId,
                                                String startDate,
                                                String endDate){
        
        return this.branchExpendRepository.findByTerminal(merchantId,
                                                          branchId,
                                                          terminalId,
                                                          startDate,
                                                          endDate);
    }

    @Override
    public List<String> insertBranchExpendTypes(List<BranchExpendType> types) {
        return this.branchExpendTypeRepository.insertBatch(types);
    }


    @Override
    public List<String> updateBranchExpendTypes(List<BranchExpendType> types) {
        return this.branchExpendTypeRepository.updateBatch(types);
    }


    @Override
    public List<String> saveBranchExpendTypes(List<BranchExpendType> types) {
        List<BranchExpendType>  newTypes = new ArrayList<BranchExpendType>();
        List<BranchExpendType>  oldTypes = new ArrayList<BranchExpendType>();
        
        splitOldFromNew(types, newTypes, oldTypes);
        ArrayList<String> failed = new ArrayList<String>();
        if (!newTypes.isEmpty()){
            failed.addAll(insertBranchExpendTypes(newTypes));
        }
        if (!oldTypes.isEmpty()){
            failed.addAll(updateBranchExpendTypes(oldTypes));
        }
        return failed;
    }
    
    @Override
    public List<BranchExpendType> findBranchExpendTypes(String merchantId,
                                                        String branchId,
                                                        String terminalId) {
        
        return this.branchExpendTypeRepository.findByTerminal(merchantId, branchId, terminalId);
    }


    @Override
    public List<String> insertBranchExpendSubjects(List<BranchExpendSubject> subjects) {
        return this.branchExpendSubjectRepository.insertBatch(subjects);
    }


    @Override
    public List<String> updateBranchExpendSubjects(List<BranchExpendSubject> subjects) {
        return this.branchExpendSubjectRepository.updateBatch(subjects);
    }


    @Override
    public List<String> saveBranchExpendSubjects(List<BranchExpendSubject> subjects) {
        List<BranchExpendSubject>  newSubjects = new ArrayList<BranchExpendSubject>();
        List<BranchExpendSubject>  oldSubjects = new ArrayList<BranchExpendSubject>();
        
        splitOldFromNew(subjects, newSubjects, oldSubjects);
        ArrayList<String> failed = new ArrayList<String>();
        if (!newSubjects.isEmpty()){
            failed.addAll(insertBranchExpendSubjects(newSubjects));
        }
        if (!oldSubjects.isEmpty()){
            failed.addAll(updateBranchExpendSubjects(oldSubjects));
        }
        return failed;
    }
    
    @Override
    public List<BranchExpendSubject> findBranchExpendSubjects(String merchantId,
                                                              String branchId,
                                                              String terminalId) {
        
        return this.branchExpendSubjectRepository.findByTerminal(merchantId, branchId, terminalId);
    }
}
